#include <windows.h>

#include "lib.h"
#include "patch.h"
#include "client.h"
#include "patchsrv.h"
#include "log.h"

#include "detreq.h"
#include "listpkg.h"
#include "dloadpkg.h"
#include "rslistpkg.h"
#include "rsdloadpkg.h"
#include "rcvpkgupdate.h"
#include "speedtest.h"
#include "changespeed.h"

Client::Client(HANDLE socket)
{
	next = prev = (Client*)0;

	net = new Net(socket, this);
	file = new File(this);

	iused = oused = msglen = 0;
	msgFormat = SINGLE_BYTE;
	msgOverhead = 1;
	reqTime = 0;
	subsend = 0;
	endtime = 0;
	bytespersec = -1;

    m_isClosed = false;

	handler[IDLE] 				= new DetermineRequest(this);
	handler[REQ_LIST] 			= new ListPackages(this);
	handler[REQ_DOWNLOAD] 		= new DownloadPackage(this);
	handler[REQ_PKG_FILELIST] 	= new RSListPackages(this);
	handler[REQ_RSYNC] 			= new RSDownloadPackage(this);
	handler[RECV_PKG_UPDATE]	= new RecievePackageUpdate(this);
	handler[SPEEDTEST]			= new SpeedTest(this);
	handler[CHANGESPEED]		= new ChangeSpeed(this);

	protocol = IDLE;

	// initially, we don't have a package.	
	m_current_package = (PackageDescription *)0;

    m_pNetCritSec = new CRITICAL_SECTION;
    if (m_pNetCritSec)
    {
		InitializeCriticalSection(m_pNetCritSec);
	}

    m_pFileCritSec = new CRITICAL_SECTION;
    if (m_pFileCritSec)
    {
		InitializeCriticalSection(m_pFileCritSec);
	}

	// luke 4/1/02: Initialize TCP keepalives on the connection
	net->initKeepAlive();
}

Client::~Client()
{
   if (!m_isClosed)
        close();

	delete file;
    file = NULL;
	delete net;
    net = NULL;

	delete handler[IDLE];
	handler[IDLE] = NULL;
	delete handler[REQ_LIST];
	handler[REQ_LIST] = NULL;
	delete handler[REQ_DOWNLOAD];
	handler[REQ_DOWNLOAD] = NULL;
	delete handler[REQ_PKG_FILELIST];
	handler[REQ_PKG_FILELIST] = NULL;
	delete handler[REQ_RSYNC];
	handler[REQ_RSYNC] = NULL;
	delete handler[RECV_PKG_UPDATE];
	handler[RECV_PKG_UPDATE] = NULL;
	delete handler[SPEEDTEST];
	handler[SPEEDTEST] = NULL;
	delete handler[CHANGESPEED];
	handler[CHANGESPEED] = NULL;

    if (m_pNetCritSec)
    {
        DeleteCriticalSection(m_pNetCritSec);
        delete m_pNetCritSec;
        m_pNetCritSec = NULL;
    }

    if (m_pFileCritSec)
    {
        DeleteCriticalSection(m_pFileCritSec);
        delete m_pFileCritSec;
        m_pFileCritSec = NULL;
    }
}

void
Client::netLock()
{
    if (m_pNetCritSec)
    {
        EnterCriticalSection(m_pNetCritSec);
    }
}

void
Client::netUnlock()
{
    if (m_pNetCritSec)
    {
        LeaveCriticalSection(m_pNetCritSec);
    }
}
void
Client::fileLock()
{
    if (m_pFileCritSec)
    {
        EnterCriticalSection(m_pFileCritSec);
    }
}

void
Client::fileUnlock()
{
    if (m_pFileCritSec)
    {
        LeaveCriticalSection(m_pFileCritSec);
    }
}

bool8
Client::initOk()
{
	bool8 ok = (bool8)(net && file && net->initOk() && file->initOk());
	int32 i;
		
	for (i = 0; ok && i < NUMPROTOCOLS; i++)
		ok = (bool8)(handler[i] != (Protocol*)0);

	return ok;
}

void
Client::ready()
{
	readNet();
}

void
Client::close()
{
	file->close();
	net->close();
    m_isClosed = true;
	reqTime = 0;
}

bool8
Client::isStale(uint32 staleAge)
{
	return (bool8)(GetTickCount() - reqTime >= staleAge);
}

void
Client::readNet()
{
    netLock();
	reqTime = GetTickCount();
	ASSERT(iused <= INMAX)
	if (!net->read(in + iused, INMAX - iused))
		close();
	netUnlock();
}

void
Client::writeNet()
{
	netLock();

	reqTime = GetTickCount();
	ASSERT(oused <= OUTMAX);

	int32 bytestosend = oused;

	if (bytestosend == 0)
	{
		netUnlock();
		return;
	}

	if (!net->write(out, bytestosend))
		close();

	if (bytespersec > 0)
		endtime = GetTickCount()+(bytestosend*1000/bytespersec);
	else
		endtime = 0;

    netUnlock();
}

void
Client::protocolDone()
{
	protocol = IDLE;

	readNet();
}

void
Client::errmsg(int32 Error)
{
	if (msgFormat == SINGLE_BYTE)
	{
		out[oused++] = (uint8)0x03;
	}
	else
	{
		out[oused++] = (uint8)0x00;
		out[oused++] = (uint8)0x04;
		out[oused++] = (uint8)0x00;
	}

	out[oused++] = (uint8)'E';
	out[oused++] = (uint8)Error;

	protocol = IDLE;

	writeNet();
}

void 
Client::eatIBuf(int32 Amt)
{
    netLock();
	ASSERT(Amt >= 0 && Amt <= INMAX);

	if (Amt >= iused)
    {
        DEBUGMSG(("Consuming all remaining %d bytes", iused));
		iused = 0;
    }
	else if (Amt > 0)
	{
        DEBUGMSG(("Consuming %d bytes leaving %d bytes", Amt, iused - Amt));
		MoveMemory(in, in + Amt, iused - Amt);
		iused -= Amt;
	}
    netUnlock();
}

void
Client::netIn(int32 Amt)
{
    netLock();
	if (Amt > 0)
	{
		DEBUGMSG(("Client::netIn(%d) -- %d bytes available", Amt, iused + Amt));
        iused += Amt;

        ASSERT(iused <= INMAX);

		handler[protocol]->netIn(Amt);
	}
	else		// other side closed the connection
    {
		close();
    }
    netUnlock();
}

void
Client::netOut(int32 Amt)
{
	ASSERT(Amt == oused);

	subsend = oused;
	oused = 0;
		
	int32 timeleft = 0;
	if (bytespersec > 0)
		timeleft = endtime-GetTickCount();

	if (timeleft > 0)
	{
		if (!PatchServer::This->addTimer(timeleft, this))
		{
			DEBUGMSG(("TIMER FAILED"));
			realNetOut(0);
		}
        else
        {
            DEBUGMSG(("Client::netOut send delayed(%d)", timeleft));
        }
	}
	else
	{
		realNetOut(0);
	}
}

void
Client::netTimer(int32 Amt)
{
	realNetOut(0);
}

void
Client::realNetOut(int32 Amt)
{
	netLock();
	handler[protocol]->netOut(subsend);
	netUnlock();
}

void
Client::netErr(uint32 Err)
{
    netLock();
	close();
    netUnlock();
}

void
Client::fileIn(int32 Amt)
{
    fileLock();
	handler[protocol]->fileIn(Amt);
    fileUnlock();
}

void
Client::fileEOF()
{
    fileLock();
	handler[protocol]->fileEOF();
    fileUnlock();
}

void
Client::fileErr(uint32 Err)
{
    fileLock();
	handler[protocol]->fileErr(Err);
    fileUnlock();
}

void
Client::msgStart()
{
	msglen = 0;			// reserve first byte to hold msglen
    memset(msgbuf, 0, sizeof(msgbuf));
}

void
Client::msgAdd(uint8 Byte)
{
	msgbuf[msglen++] = Byte;
    ASSERT(msglen < OUTMAX);
}

uint8*
Client::msgGetBuf()
{
	return msgbuf + msglen;
}

void
Client::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
    ASSERT(msglen < OUTMAX);
}

bool8
Client::msgEnd()
{
	if (oused + msglen + 3 > OUTMAX)
    {
		return false;
    }

	if (msgFormat == SINGLE_BYTE)
	{
		if (msglen > 255)
			return true;

		msglen = encode(msgbuf, out + oused + 1, msglen);
		msglen++;	// include msg len byte
		out[oused] = (uint8)(msglen & 0xff);
		oused += msglen;
	}
	else
	{
		// "+ 3" saves room for the null byte and the 2 byte message length.
		msglen = encode(msgbuf, out + oused + 3, msglen);

		// "+ 2" accounts for the 2 byte message length.  It does not account
		// for the null byte - which should be hidden from all code which uses
		// these routines.
		msglen += 2;

		out[oused + 0] = 0;
		out[oused + 1] = (uint8)((msglen >> 0) & 0xff);
		out[oused + 2] = (uint8)((msglen >> 8) & 0xff);
		oused += msglen + 1;
	}

	return true;
}

void
Client::msgAdd(uint8* buf, int32 len)
{
	memcpy(msgbuf + msglen, buf, len);
	msglen += len;
}

void
Client::msgAdd(int32 amt)
{
	msgbuf[msglen++] = (uint8)((amt >> 24) & 0xff);
	msgbuf[msglen++] = (uint8)((amt >> 16) & 0xff);
	msgbuf[msglen++] = (uint8)((amt >> 8)  & 0xff);
	msgbuf[msglen++] = (uint8)((amt >> 0)	& 0xff);

}
void
Client::msgAdd(uint16 Amt)
{
	msgbuf[msglen++] = (uint8)((Amt >> 8) & 0xff);
	msgbuf[msglen++] = (uint8)(Amt & 0xff);
}

void
Client::msgAdd(const String& str, bool8 unicode)
{

	if (unicode)
	{
		msglen += str.serialize(msgbuf + msglen);
	}
	else
	{
		int len = str.getLength();
		char* buf;

		// The code base is unicode, but we still only xfer ASCII across the wire
		buf = new char[len+1];
		StringConversions::convert(buf, str, len+1);
		msgAdd((uint8)(len & 0xff));
		msgAdd((uint8*)buf, len);

		delete buf;
	}
}

int32 
Client::encode(uint8* in, uint8* out, int32 len)
{
	if (len > 0)
	{
		if (in != out)
			memcpy(out, in, len);

		return len;
	}

	return 0;
}


uint8
Client::msgGet(uint8 * buffer, uint8 * data)
{
	uint8	value;

	value = buffer[0];

	if (data)
		*data = value;

	return value;
}

uint16
Client::msgGet(uint8 * buffer, uint16 * data)
{
	uint16	value;

	value = (buffer[0] <<  0) | (buffer[1] <<  8);

	if (data)
		*data = value;

	return value;
}

uint32
Client::msgGet(uint8 * buffer, uint32 * data)
{
	uint32	value;

	value =	(buffer[0] <<  0) | (buffer[1] <<  8) |
			(buffer[2] << 16) | (buffer[3] << 24);

	if (data)
		*data = value;

	return value;
}
